package cn.syy.service.impl;


import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.syy.constants.Constants;
import cn.syy.constants.GlobalExceptionCode;
import cn.syy.feign.AppLoginFeignClient;
import cn.syy.mapper.DriverAuthMaterialMapper;
import cn.syy.mapper.DriverMapper;
import cn.syy.pojo.domain.*;
import cn.syy.pojo.dto.ChangeDriverLocationToGeoDto;
import cn.syy.pojo.dto.DriverDaySummaryDto;
import cn.syy.pojo.param.RegisterSaveLoginParam;
import cn.syy.pojo.properties.DriverSettingProperties;
import cn.syy.service.IDriverService;
import cn.syy.service.IDriverSettingService;
import cn.syy.service.IDriverSummaryService;
import cn.syy.service.IDriverWalletService;
import cn.syy.template.AppWeChatTemplate;
import cn.syy.utils.AssertUtil;
import cn.syy.utils.NameUtil;
import cn.syy.utils.SmsUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.TimeUnit;


/**
 * <p>
 * 司机对象 服务实现类
 * </p>
 *
 * @author sysyy
 * @since 2024-07-27
 */
@Service
public class DriverServiceImpl extends ServiceImpl<DriverMapper, Driver> implements IDriverService {
    @Autowired
    private IDriverWalletService driverWalletService;
    @Autowired
    private IDriverSummaryService driverSummaryService;
    @Autowired
    private IDriverSettingService driverSettingService;
    @Autowired
    private DriverSettingProperties driverSettingProperties;
    @Autowired
    private AppWeChatTemplate appWeChatTemplate;

    @Autowired
    private AppLoginFeignClient appLoginFeignClient;
    @Autowired
    private DriverAuthMaterialMapper driverAuthMaterialMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 查询司机实名认证信息
     * @return
     */
    @Override
    public DriverAuthMaterial realAuthInfo() {
        // 获取最新的实名认证信息审核
        DriverAuthMaterial driverAuthMaterial = driverAuthMaterialMapper.selectOne(new LambdaQueryWrapper<DriverAuthMaterial>()
                .eq(DriverAuthMaterial::getDriverId, StpUtil.getLoginIdAsLong())
                .orderByDesc(DriverAuthMaterial::getCreateTime)
                .orderByDesc(DriverAuthMaterial::getCreateTime)
                .last("limit 1"));
        return driverAuthMaterial;
    }

    /**
     * 查询司机今日数据统计
     * @return
     */

    @Override
    public DriverDaySummaryDto daySummary() {
        // 根据登录id获取司机今日统计数据并且判断是否有这个司机对象
        DriverSummary driverSummary = driverSummaryService.getById(StpUtil.getLoginIdAsLong());
        AssertUtil.isNotNull(driverSummary,GlobalExceptionCode.SERVICE_ERROR);
        // 复制司机统计的数据到DriverDaySummaryDto对象
        return BeanUtil.copyProperties(driverSummary, DriverDaySummaryDto.class);
    }

    /**
     * 小程序司机开始接单上线
     */
    @Override
    public void online() {
        // 业务校验：1. 判断是否有当前司机的配置信息
        long loginId= StpUtil.getLoginIdAsLong();
        DriverSetting driverSetting = driverSettingService.getById(loginId);
        AssertUtil.isNotNull(driverSetting,GlobalExceptionCode.SERVICE_ERROR);
        // 业务实现： 1. 将当前司机的配置信息存储到redis中
        String key =String.format(Constants.Redis.DRIVER_ONLINE_KEY, loginId) ;
        // TODO: 2024/8/8  上线后设置更新时间为半个小时
        redisTemplate.opsForValue().set(key,driverSetting, 10,TimeUnit.HOURS);
    }
    /**
     * 小程序司机停止接单下线
     */
    @Override
    public void offline() {
        String loginId = StpUtil.getLoginIdAsString();
        String key =String.format(Constants.Redis.DRIVER_ONLINE_KEY, loginId) ;
        // 业务实现: 1. 将司机的配置信息从redis中移除出去
        redisTemplate.delete(key);
        // 2.，删除当前那司机的GEO位置
        redisTemplate.opsForGeo().remove(Constants.Redis.DRIVER_LOCATION_KEY,loginId);
    }

    /**
     * 小程序司机位置更新到Redis——Geo
     * @param changeDriverLocationToGeoDto
     */

    @Override
    public void changeLocationToGeo(ChangeDriverLocationToGeoDto changeDriverLocationToGeoDto) {
        // 业务校验：校验司机的当前位置是否早缓存中，在的话就不更新到缓存了
        String loginId = StpUtil.getLoginIdAsString();
        String key =String.format(Constants.Redis.DRIVER_ONLINE_KEY, loginId) ;
        Object driverSetting = redisTemplate.opsForValue().get(key);
        if ( ObjectUtil.isNull(driverSetting)){
            return;
        }
        // 业务实现： 将当前司机的位置添加到Redis——Geo中
        Point point = new Point(changeDriverLocationToGeoDto.getLongitude(), changeDriverLocationToGeoDto.getLatitude());
        redisTemplate.opsForGeo().add(Constants.Redis.DRIVER_LOCATION_KEY,point,loginId);
    }

    /**
     * 小程序司机注册（包含手机号）
     * @param openIdCode
     * @param phoneCode
     */
    @Override
    public void registerIncludePhoneCode(String openIdCode, String phoneCode) {
        String openid = appWeChatTemplate.getOpenIdByCode(openIdCode);
        Driver driver = super.getOne(new QueryWrapper<Driver>().eq("open_id", openid)); // 通过openid获取到driver对象

        AssertUtil.isNull(driver,GlobalExceptionCode.PARAM_PHONE_EXIST); // 判断diver对象是否存在，存在就说名已经注册过了，就为空

        // 3. 业务实现
        // 3.1 将注册的司机对象放到数据库
        String phoneNumber = appWeChatTemplate.getPhoneByAccessTokenAndCode(phoneCode);
        driver = new Driver();
        saveDriverIncludePhone(driver,openid,phoneNumber);

        // 3.2 获取随机密码并发送给手机主人
        SmsUtils.setPassword();
        String password = SmsUtils.getPassword();
//        SmsUtils.sendSms(phoneNumber,password);

        // 3.3 随机密码进行加密
        String encryptedPassword = BCrypt.hashpw(password, BCrypt.gensalt());

        // 3.4将从表（配置，钱包，统计）进行初始化
        Long id = driver.getId();
        initDriverAssociation(id,encryptedPassword);

        // 3.5 将登录信息保存到login数据库中
        saveLoginIncludePhone(openid,encryptedPassword,phoneNumber,id);
    }

    /**
     * 小程序司机注册（不包含手机号）
     * @param openIdCode
     */
    @Override
    @Transactional
    public void register(String openIdCode) {
        String openid = appWeChatTemplate.getOpenIdByCode(openIdCode);
        Driver driver = super.getOne(new QueryWrapper<Driver>().eq("open_id", openid)); // 通过openid获取到driver对象
        AssertUtil.isNull(driver, GlobalExceptionCode.PARAM_PHONE_EXIST); // 判断diver对象是否存在，存在就说名已经注册过了，就为空
//         3. 业务实现
//         3.1 将注册的司机对象放到数据库
        driver = new Driver();
        String phoneNumber = "15611810650"; // 设置接收短信的手机号
        driver.setPhone(phoneNumber);
        saveDriverIncludePhone(driver,openid,phoneNumber);

        // 发送密码给手机号主人
        SmsUtils.setPassword();
        String password = SmsUtils.getPassword();
        String encryptedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
//        SmsUtils.sendSms(phoneNumber,password);

        // 3.2将从表（配置，钱包，统计）进行初始化
        Long id = driver.getId();
        initDriverAssociation(id,encryptedPassword);

        // 3.3 将登录信息保存到login数据库中
        saveLoginIncludePhone(openid,encryptedPassword,phoneNumber,id);

    }


    /**
     * 存储注册司机信息到uaa的login表里（包含手机号）
     * @param openid
     */
    private void saveLoginIncludePhone(String openid,String password,String phoneNumber,Long id) {
        RegisterSaveLoginParam registerSaveLoginParam = new RegisterSaveLoginParam();
        String name = NameUtil.getName();
        registerSaveLoginParam.setUsername(name);
        registerSaveLoginParam.setPassword(password);
        registerSaveLoginParam.setType(Constants.Login.TYPE_DRIVER);
        registerSaveLoginParam.setEnabled(true); // 账号是否启用
        registerSaveLoginParam.setAvatar(Constants.AVATAR);
        registerSaveLoginParam.setAdmin(false); // 是否是管理员
        registerSaveLoginParam.setNickName(name);
        registerSaveLoginParam.setOpenId(openid);
        registerSaveLoginParam.setName(name);
        registerSaveLoginParam.setPhone(phoneNumber);
        registerSaveLoginParam.setId(id);
        appLoginFeignClient.save(registerSaveLoginParam);
    }

    /**
     * 将注册的司机对象放到driver数据库表（包含手机号)
     */
    private void saveDriverIncludePhone(Driver driver, String openid, String phoneNumber) {
        driver.setPhone(phoneNumber);
//        driver.setArchive(false);
//        driver.setCreateTime(new Date());
//        driver.setDeleted(false);
//        driver.setVersion(0);
        driver.setOpenId(openid);
//        driver.setBitState(0L);
        super.save(driver);
    }

    /**
     * 初始化司机从表：因为代码过长所以需要抽取代码，就把代码都在实体类里进行初始化
     * */
    public void initDriverAssociation(Long id,String password){
        DriverWallet driverWallet = new DriverWallet();
        driverWallet.setId(id);
//        driverWallet.setAmount(new BigDecimal("0"));
//        // 生成随机密码并发送短信验证码告诉用户
        driverWallet.setPassword(password);
//        driverWallet.setDeleted(false);
//        driverWallet.setVersion(0);
//        driverWallet.setCreateTime(new Date());
        driverWalletService.save(driverWallet);

        DriverSetting driverSetting = new DriverSetting();
        driverSetting.setId(id);
//        driverSetting.setAutoAccept(false);
//        driverSetting.setOrientation(false);
//        driverSetting.setListenService(false);
        driverSetting.setOrderDistance(driverSettingProperties.getOrderDistance());
        driverSetting.setRangeDistance(driverSettingProperties.getRangeRistance());
//        driverSetting.setCreateTime(new Date());
//        driverSetting.setDeleted(false);
//        driverSetting.setVersion(0);
        driverSettingService.save(driverSetting);

        DriverSummary driverSummary = new DriverSummary();
        driverSummary.setId(id);
//        driverSummary.setLevel(0);
//        driverSummary.setTotalOrders(0);
//        driverSummary.setWeekOrders(0);
//        driverSummary.setGoodComments(0);
//        driverSummary.setAppeal(0);
//        driverSummary.setTotalComplaint(0);
//        driverSummary.setTodayTotalCancel(0);
//        driverSummary.setDriveDuration(0);
//        driverSummary.setTradeOrders(0);
//        driverSummary.setTodayTradeOrders(0);
//        driverSummary.setTodayComplaint(0);
//        driverSummary.setTodayCancel(0);
//        driverSummary.setTodayIncome(new BigDecimal("0"));
//        driverSummary.setCreateTime(new Date());
//        driverSummary.setDeleted(false);
//        driverSummary.setVersion(0);

        driverSummaryService.save(driverSummary);

    }



//     // Dto：后端之间传递参数所使用的临时对象，vo用于接收前端参数的临时对象
//    // 小程序司机注册（不包含手机号）
//    @Override
//    @Transactional
//    public void register(String openIdCode) {
//        // 1. 参数校验 （使用JSR303完成参数校验，controller里已经完成）
//        // 2. 业务校验
//        // 2.1 判断司机是否注册过
//        // CODE_TO_OPENID_URL_TEMPLATE = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"; 剩下两个参数是获取小程序上openid方法的参数
//        // 使用%s作为占位符，然后再用String.format字符串使用占位符的位置的内容给添加上
//        String url = String.format(Constants.url.CODE_TO_OPENID_URL_TEMPLATE, appSettingProperties.getAppId(), appSettingProperties.getSecret(), openIdCode);
//        String jsonString = HttpUtil.get(url); // hutool工具包发送请求的方式，返回的是一个json格式的字符串
//        CodeToOpenIdDto codeToOpenIdDto = JsonToClassUtil.objJsonStr2Class(jsonString, CodeToOpenIdDto.class); // 将json对象转换为创建的实体类对象
//        String openid = codeToOpenIdDto.getOpenid();
//        // if语句里面如果是抛出异常就使用断言工具类
//        AssertUtil.isNotEmpty(openid,GlobalExceptionCode.PARAM_CODE_ERROR); // 判断openid是否为空，为空的话就抛出异常
////        if (openid.isBlank()){
////            throw new GlobalException(GlobalExceptionCode.PARAM_CODE_ERROR);
////        }
//        Driver driver = super.getOne(new QueryWrapper<Driver>().eq("open_id", openid)); // 通过openid获取到driver对象
////        LambdaQueryWrapper<Driver> driver = new LambdaQueryWrapper<Driver>().eq(Driver::getOpenId, openid);
//        AssertUtil.isNull(driver,GlobalExceptionCode.PARAM_PHONE_EXIST); // 判断diver对象是否存在，存在就说名已经注册过了，就为空
////        if (ObjectUtil.isNotNull(driver)){
////            throw new GlobalException(GlobalExceptionCode.PARAM_PHONE_EXIST);
////        }
//        // 3. 业务实现
//        // 3.1 将注册的司机对象放到数据库
//        driver = new Driver();
//        saveDriver(driver,openid);
//
//        // 将从表（配置，钱包，统计）进行初始化
//        Long id = driver.getId();
//        initDriverAssociation(id);
//    }
//
//    // 初始化司机从表：因为代码过长所以需要抽取代码，就把代码都在实体类里进行初始化
//    public void initDriverAssociation(Long id){
//        DriverWallet driverWallet = new DriverWallet();
//        driverWallet.setId(id);
////        driverWallet.setAmount(new BigDecimal("0"));
////        // 生成随机密码并发送短信验证码告诉用户
////        driverWallet.setPassword("123456");
////        driverWallet.setDeleted(false);
////        driverWallet.setVersion(0);
////        driverWallet.setCreateTime(new Date());
//        driverWalletService.save(driverWallet);
//
//        DriverSetting driverSetting = new DriverSetting();
//        driverSetting.setId(id);
////        driverSetting.setAutoAccept(false);
////        driverSetting.setOrientation(false);
////        driverSetting.setListenService(false);
//        driverSetting.setOrderDistance(driverSettingProperties.getOrderDistance());
//        driverSetting.setRangeDistance(driverSettingProperties.getRangeRistance());
////        driverSetting.setCreateTime(new Date());
////        driverSetting.setDeleted(false);
////        driverSetting.setVersion(0);
//        driverSettingService.save(driverSetting);
//
//        DriverSummary driverSummary = new DriverSummary();
//        driverSummary.setId(id);
////        driverSummary.setLevel(0);
////        driverSummary.setTotalOrders(0);
////        driverSummary.setWeekOrders(0);
////        driverSummary.setGoodComments(0);
////        driverSummary.setAppeal(0);
////        driverSummary.setTotalComplaint(0);
////        driverSummary.setTodayTotalCancel(0);
////        driverSummary.setDriveDuration(0);
////        driverSummary.setTradeOrders(0);
////        driverSummary.setTodayTradeOrders(0);
////        driverSummary.setTodayComplaint(0);
////        driverSummary.setTodayCancel(0);
////        driverSummary.setTodayIncome(new BigDecimal("0"));
////        driverSummary.setCreateTime(new Date());
////        driverSummary.setDeleted(false);
////        driverSummary.setVersion(0);
//
//        driverSummaryService.save(driverSummary);
//
//    }
//
//    // 创建司机对象到数据库
//    public void saveDriver(Driver driver,String openid){
////        driver.setPhone("");
////        driver.setArchive(false);
////        driver.setCreateTime(new Date());
////        driver.setDeleted(false);
////        driver.setVersion(0);
//        driver.setOpenId(openid);
////        long bitState = BitStatesUtil.addState(BitStatesUtil.OP_DRIVER_AGREEMENT, BitStatesUtil.OP_PHONE); // 参数值为(32 ,1) ，最后的为状态值为 33
////        long bitState1 = BitStatesUtil.removeState(bitState,BitStatesUtil.OP_DRIVER_AGREEMENT); //bitState=33,参数值为(33,32) ，最后的为状态值为 1
//        driver.setBitState(BitStatesUtil.OP_PHONE); // 位状态设置初始等级
//        super.save(driver);
//
//    }
//
//    // 小程序司机注册（包含手机号）
//    @Override
//    public void registerIncludePhoneCode(String openIdCode, String phoneCode) {
//        // 1. 参数校验 （使用JSR303完成参数校验，controller里已经完成）
//        // 2. 业务校验
//        // 2.1 判断司机是否注册过
//        // CODE_TO_OPENID_URL_TEMPLATE = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"; 剩下两个参数是获取小程序上openid方法的参数
//        // 使用%s作为占位符，然后再用String.format字符串使用占位符的位置的内容给添加上
//        String url = String.format(Constants.url.CODE_TO_OPENID_URL_TEMPLATE, appSettingProperties.getAppId(), appSettingProperties.getSecret(), openIdCode);
//        String jsonString = HttpUtil.get(url); // hutool工具包发送请求的方式，返回的是一个json格式的字符串
//        CodeToOpenIdDto codeToOpenIdDto = JsonToClassUtil.objJsonStr2Class(jsonString, CodeToOpenIdDto.class); // 将json对象转换为创建的实体类对象
//        String openid = codeToOpenIdDto.getOpenid();
//        // if语句里面如果是抛出异常就使用断言工具类
//        AssertUtil.isNotEmpty(openid,GlobalExceptionCode.PARAM_CODE_ERROR); // 判断openid是否为空，为空的话就抛出异常
////        if (openid.isBlank()){
////            throw new GlobalException(GlobalExceptionCode.PARAM_CODE_ERROR);
////        }
//        Driver driver = super.getOne(new QueryWrapper<Driver>().eq("open_id", openid)); // 通过openid获取到driver对象
////        LambdaQueryWrapper<Driver> driver = new LambdaQueryWrapper<Driver>().eq(Driver::getOpenId, openid);
//        AssertUtil.isNull(driver,GlobalExceptionCode.PARAM_PHONE_EXIST); // 判断diver对象是否存在，存在就说名已经注册过了，就为空
////        if (ObjectUtil.isNotNull(driver)){
////            throw new GlobalException(GlobalExceptionCode.PARAM_PHONE_EXIST);
////        }
//        // 3. 业务实现
//        // 3.1 将注册的司机对象放到数据库
//        String phoneNumber = getPhoneNumber(phoneCode);
//        driver = new Driver();
//        saveDriverIncludePhone(driver,openid,phoneNumber);
//
//        // 将从表（配置，钱包，统计）进行初始化
//        Long id = driver.getId();
//        initDriverAssociation(id);
//    }
//
//    private void saveDriverIncludePhone(Driver driver, String openid, String phoneNumber) {
//        driver.setPhone(phoneNumber);
////        driver.setArchive(false);
////        driver.setCreateTime(new Date());
////        driver.setDeleted(false);
////        driver.setVersion(0);
//        driver.setOpenId(openid);
////        driver.setBitState(0L);
//        super.save(driver);
//    }
//
//    private String getPhoneNumber(String phoneCode) {
//        // 准备正确的发送获取accessToken的请求地址
//        String accessTokenUrl = String.format(Constants.url.CODE_TO_ACCESS_TOKEN_URL_TEMPLATE, appSettingProperties.getAppId(), appSettingProperties.getSecret());
//        // 发送获取accessToken的get请求并且返回的是一个json形式的字符串
//        String  resultForAccessToken= HttpUtil.get(accessTokenUrl);
//        // 将json形式的字符串转换为json形式的对象
//        JSONObject jsonResult = JSONUtil.toBean(resultForAccessToken, JSONObject.class);
//        // 获取json对象里面的access_token的值。并且判断是否为空，为空过的话就抛出异常（字符串形式需要判断是否为null和空值，其他形式就可以只需判断是否为null）
//        String accessToken = jsonResult.getStr("access_token");
//        AssertUtil.isNotEmpty(accessToken,GlobalExceptionCode.PARAM_CODE_ERROR);
//        // 准备正确的发送获取手机号的请求地址
//        String phoneUrl = String.format(Constants.url.CODE_TO_PHONE_URL_TEMPLATE, accessToken);
//        // 发送请求有一个json格式的code参数，所以需要创建一个包含code的json格式的对象
//        JSONObject body = new JSONObject();
//        body.set("code",phoneCode);
//        //发送获取手机号的post请求
//        String resultForPhone = HttpUtil.post(phoneUrl, body);
//        JSONObject jsonResultForPhone = JSONUtil.parseObj(resultForPhone);
//        // 获取json对象的errcode字段并且判断是否为0，不为0的话就抛出异常，为0就说明获取正确
//        Integer errcode = jsonResultForPhone.getInt("errcode");
//        AssertUtil.isEquals(0,errcode,GlobalExceptionCode.PARAM_CODE_ERROR);
//        // 获取jsonResultForPhone里面的json对象phone_info，并且获取里面的phoneNumber的值（字符串形式），并且判断是否为空，为空就抛出异常
//        JSONObject phoneInfo = jsonResultForPhone.getJSONObject("phone_info");
//        String phoneNumber = phoneInfo.getStr("phoneNumber");
//        AssertUtil.isNotEmpty(phoneNumber,GlobalExceptionCode.PARAM_PHONE_ERROR);
//        return phoneNumber;
//    }
// 小程序司机注册（不包含手机号）


}
